package org.biubiu0002.baiblog.im;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import lombok.extern.slf4j.Slf4j;
import org.biubiu0002.baiblog.im.handler.MessageRedirectHandler;
import org.biubiu0002.baiblog.im.handler.HttpAuthRequestHandler;
import org.biubiu0002.baiblog.im.handler.WebSocketHandShakeHandler;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * @Author: biubiu0002
 * @Date: 2022/9/1 23:53
 **/
@Component
@Slf4j
public class NettyServer {

    @Resource
    private HttpAuthRequestHandler httpAuthRequestHandler;
    @Resource
    private WebSocketHandShakeHandler webSocketHandShakeHandler;

    @Resource
    private MessageRedirectHandler messageRedirectHandler;

    public NettyServer(){}


    public void run(){
        NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
        NioEventLoopGroup workGroup = new NioEventLoopGroup();
        ServerBootstrap b = new ServerBootstrap();
        try {
            b.group(bossGroup,workGroup);
            b.option(ChannelOption.ALLOCATOR, ByteBufAllocator.DEFAULT);
            b.option(ChannelOption.SO_KEEPALIVE,true);
            b.localAddress(9527);
            b.channel(NioServerSocketChannel.class);
            b.childHandler(new ChannelInitializer<NioSocketChannel>() {
                @Override
                protected void initChannel(NioSocketChannel channel) throws Exception {
                    ChannelPipeline p = channel.pipeline();
                    //http报文解码
                    p.addLast("httpDecoder",new HttpRequestDecoder());
                    //http报文编码
                    p.addLast("httpEncoder",new HttpResponseEncoder());
                    //http聚合 聚合HTTP请求行 请求头 请求体 聚合成完整报文
                    p.addLast("httpAggregator",new HttpObjectAggregator(65535));
                    //异步发送大数据流
                    p.addLast("chunkedWriter",new ChunkedWriteHandler());
                    //http登录验证处理器
                    p.addLast("httpAuth",httpAuthRequestHandler);
                    //websocket协议处理器 包括ping pong 心跳检测
                    p.addLast("websocketHandler",new WebSocketServerProtocolHandler("/chat",null,
                            true,10*1024,
                            true,true));
                    //websocket帧前置处理器
                    p.addLast("websocketHandShake", webSocketHandShakeHandler);
//                    p.addLast(new WebSocketFrameHandler)
                    //聊天处理器
                    p.addLast("chatProcessor", messageRedirectHandler);
                }
            });
            ChannelFuture channelFuture = b.bind().sync();
            log.info("服务器启动成功,监听端口: {}", channelFuture.channel().localAddress());
            //服务监听通道会一直等待通道关闭的异步任务结束
            ChannelFuture closeFuture = channelFuture.channel().closeFuture();
            closeFuture.sync();

        }catch (Exception e){
            log.error("服务器启动失败",e);
        }finally {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
}
